
#include "uli.hpp"
#include <unordered_map>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "threadpool.hpp"
#include "task.hpp"
using namespace std;

/*

    ssize_t s=recvfrom(listen_sockfd_,recvbuff,sizeof(recvbuff)-1,0,(struct sockaddr*)&client,&len);
    if(s>0){
    recvbuff[s]=0;
    }
    else if(s==-1)
    {
                    logMessage(WARINING,"recvform:%s:%d",strerror(errno),listen_sockfd_);
                    continue;
                }
                // 读取成功的,除了读取到对方的数据，还要读取到对方的网络地址[ip:port]
                std::string clientIp = inet_ntoa(client.sin_addr);       //拿到了对方的IP
                uint32_t clientPort = ntohs(client.sin_port); // 拿到了对方的port

                checkOnlineUser(clientIp, clientPort, client); //如果存在，什么都不做，如果不存在，就添加
                // 打印出来客户端给服务器发送过来的消息
                logMessage(NOTICE, "[%s:%d]# %s", clientIp.c_str(), clientPort, recvbuff);

                //实现广播
                messageRoute(clientIp,clientPort,recvbuff);
*/
class Tcpserver;
class arglist
{
public:
    struct sockaddr_in *_addr;
    int _sockfd;
    Tcpserver *_svr;
    //构造函数
    arglist(int sockfd, Tcpserver *svr, struct sockaddr_in *addr)
        : _sockfd(sockfd), _svr(svr), _addr(addr)
    {
    }
};

static void Usage(const std::string porc)
{
    std::cout << "Usage:\n\t" << porc << " port [ip]" << std::endl;
}

class Tcpserver;
std::unordered_map<std::string, int> users_sockfd;
//实现广播
void messageRoute(std::string ip, uint32_t port, std::string info)
{

    std::string message = "[";
    message += ip;
    message += ":";
    message += std::to_string(port);
    message += "]# ";
    message += info;
    for (auto &sockfd : users_sockfd)
    {
        write(sockfd.second, message.c_str(), strlen(message.c_str()));
    }
}
void info_to_client(const std::string clientip, uint32_t clientport, int sock)
{
    assert(sock >= 0);
    assert(!clientip.empty());
    assert(clientport >= 1024);
    char buff[1024];
    logMessage(DEBUG, "begin..................%s[%d]", clientip.c_str(), sock);
    while (true)
    {
        ssize_t s = read(sock, buff, sizeof(buff) - 1);
        if (s > 0)
        {
            //判断用户是否需要退出
            buff[s] = '\0';
            if (strcasecmp(buff, "quit") == 0)
            {
                logMessage(DEBUG, "client quit---%s[%d]", clientip.c_str(), sock);
                break;
            }
            logMessage(DEBUG, "%s[%d] information  :%s", clientip.c_str(), sock, buff);
            //实现广播
            messageRoute(clientip, clientport, buff);
        }
        else if (s == 0)
        {
            // pipe: 读端一直在读，写端不写了，并且关闭了写端，读端返回s == 0，代表对端关闭
            // s == 0: 代表对方关闭,client 退出
            logMessage(DEBUG, "client quit-----%s[%d]", clientip.c_str(), sock);
            break;
        }
        else
        {
            logMessage(DEBUG, "%s[%d] - read: %s", clientip.c_str(), clientport, strerror(errno));
            break;
        }
    }
    logMessage(DEBUG, "end..................%s[%d]", clientip.c_str(), sock);
    close(sock);
    logMessage(DEBUG, "server close %d done", sock);
}

class Tcpserver
{
public:
    Tcpserver(uint16_t port, const std::string ip = "")
        : port_((uint16_t)port), ip_(ip), listen_sockfd_(-1)
    {
        pthread_mutex_init(&_mutex, nullptr);
    }
    ~Tcpserver()
    {
    }
    //初始化接口
    void init()
    {
        listen_sockfd_ = socket(PF_INET, SOCK_STREAM, 0);
        if (listen_sockfd_ < 0)
        {
            logMessage(FATAL, "socket:%s:%d", strerror(errno), listen_sockfd_);
            exit(1);
        }
        logMessage(DEBUG, "create socket sucess: %d", listen_sockfd_);
        //绑定端口
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = PF_INET;
        local.sin_port = htons(port_);
        // 服务器都必须具有IP地址,"xx.yy.zz.aaa",字符串风格点分十进制 -> 4字节IP -> uint32_t ip
        // INADDR_ANY(0): 程序员不关心会bind到哪一个ip， 任意地址bind，强烈推荐的做法，所有服务器一般的做法
        // inet_addr: 指定填充确定的IP，特殊用途，或者测试时使用，除了做转化，还会自动给我们进行 h—>n
        //绑定任意端口
        ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));
        int ret = bind(listen_sockfd_, (const struct sockaddr *)&local, sizeof(local));
        if (ret < 0)
        {
            logMessage(FATAL, "bind:%s", strerror(errno));
            exit(2);
        }
        logMessage(DEBUG, "bind sucess:%d", listen_sockfd_);
        //监听
        int res = listen(listen_sockfd_, 5);
        if (res < 0)
        {
            logMessage(FATAL, "listen:%s", strerror(errno));
            exit(3);
        }
        logMessage(DEBUG, "listen sucess:%d", listen_sockfd_);
    }

    //检测用户是否已经被添加
    void checkOnlineUser(std::string &clientip, uint32_t clientport, struct sockaddr_in &client)
    {
        std::string key = clientip;
        key += ":";
        key += std::to_string(clientport);
        auto iter = users.find(key);
        if (iter == users.end())
        {
            users.insert({key, client});
        }
        else
        {

            // do nothing
        }
    }

    //接收消息，实现广播
    void start()
    {
        //注册信号捕捉
        // signal(SIGCHLD, SIG_IGN);
        //创建线程池
        pool_ = threadpool<task>::getInstance();
        //发送缓存和接收缓存
        char recvbuff[1024] = {0};
        char sendbuff[1024] = {0};
        pool_->start();
        while (true)
        {
            //提取连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sock = accept(listen_sockfd_, (sockaddr *)&client, &len);
            if (sock < 0)
            {
                logMessage(WARINING, "accept:%s[%d]", strerror(errno), sock);
            }
            logMessage(DEBUG, "accept success: [%d]", sock);

            //获取客户端的IP和PORT信息
            std::string clientIp = inet_ntoa(client.sin_addr);
            uint32_t clientport = ntohs(client.sin_port);

            //添加客户与套接字中间的映射
            users_sockfd[clientIp] = sock;
            //创建任务
            task t(clientIp, clientport, sock, info_to_client);
            pool_->push(t);
            /*
            pid_t pid = fork();
            if (pid == 0)
            { //子进程
                close(listen_sockfd_);
                //孙子进程实现业务逻辑，子进程负责创建孙子进程

                //子进程
                if (fork() > 0)
                {
                    exit(0);
                }
                //孙子进程，与客户端进行通信
                info_to_client(clientIp, clientport, sock);
                exit(0);
            }
            //父进程回收子进程
            close(sock);
            pid_t ret = waitpid(pid, nullptr, 0); //阻塞方式回收子进程
            */
            /*
            if(pid==0){ //子进程与客户端进行通信
                //子进程再添加一次，父子进程会发生写时拷贝
                users_sockfd[clientIp]=sock;
                close(listen_sockfd_);
                checkOnlineUser(clientIp,clientport,client);
                //与客户端进行通信
                info_to_client(clientIp,clientport,sock);
                exit(0);
            }
            else{   //父进程需要关闭子进程通信的套接字，继续提取链接
                close(sock);
                continue;
            }
            */
            /*
            //多线程版本
            arglist *arg = new arglist(sock, this, &client);
            users_sockfd[clientIp] = sock;
            //创建一个线程，用于对该套接字的运行
            pthread_t pid;
            pthread_create(&pid, nullptr, pthread_run, (void *)arg);
            */
        }
    }

    /*
    //静态函数内部，必须是某个对象调用具体的成员函数；
    static void *pthread_run(void *arg)
    {
        pthread_detach(pthread_self());
        arglist *arl = (arglist *)arg;
        //注册用户
        struct sockaddr_in *client = arl->_addr;
        socklen_t len = sizeof(client);
        // 读取成功的,除了读取到对方的数据，还要读取到对方的网络地址[ip:port]
        std::string clientIp = inet_ntoa((*client).sin_addr); //拿到了对方的IP
        uint32_t clientPort = ntohs((*client).sin_port);      // 拿到了对方的port
        arl->_svr->lock();
        arl->_svr->checkOnlineUser(clientIp, clientPort, *client); //如果存在，什么都不做，如果不存在，就添加
        arl->_svr->unlock();
        //循环接收数据
        char recvbuff[1024];
        while (true)
        {
            bzero(&recvbuff, sizeof(recvbuff));
            //接收消息
            ssize_t s = read(arl->_sockfd, recvbuff, sizeof(recvbuff) - 1);
            // 打印出来客户端给服务器发送过来的消息
            logMessage(NOTICE, "[%s:%d]# %s", clientIp.c_str(), clientPort, recvbuff);
            //实现广播
            arl->_svr->messageRoute(clientIp, clientPort, recvbuff);
        }
    }
    */
    void lock()
    {
        pthread_mutex_lock(&_mutex);
    }
    void unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

private:
    int listen_sockfd_; //套接字
    uint16_t port_;
    std::string ip_;
    std::unordered_map<std::string, struct sockaddr_in> users; //记录在线用户
    pthread_mutex_t _mutex;
    threadpool<task> *pool_;
};

int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }
    Tcpserver svr(port, ip);
    svr.init();
    svr.start();
    return 0;
}

